package com.ddwl.shop.service;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ddwl.common.constant.BaseEnum;
import com.ddwl.common.exception.ParamException;
import com.ddwl.common.feign.api.GateApi;
import com.ddwl.common.feign.api.UserApi;
import com.ddwl.common.util.BeanConverUtils;
import com.ddwl.common.util.PositionUtil;
import com.ddwl.schema.bo.shop.*;
import com.ddwl.schema.vo.gate.RegionVo;
import com.ddwl.schema.vo.shop.DistributionFeeVo;
import com.ddwl.schema.vo.shop.ProvinceVo;
import com.ddwl.schema.vo.shop.ShopDistributionVo;
import com.ddwl.schema.vo.user.UserAddrVo;
import com.ddwl.shop.constant.ShopDistributionEnum;
import com.ddwl.shop.dao.DistributionConfigDao;
import com.ddwl.shop.dao.ShopDao;
import com.ddwl.shop.dao.ShopDistributionDao;
import com.ddwl.shop.dao.model.DistributionConfig;
import com.ddwl.shop.dao.model.Shop;
import com.ddwl.shop.dao.model.ShopDistribution;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author hemiaojun
 * @Date 2021/5/6 13:57
 */
@Service
public class ShopDistributionService {

    @Resource
    private ShopDistributionDao shopDistributionDao;
    @Resource
    private DistributionConfigDao distributionConfigDao;
    @Resource
    private ShopDao shopDao;
    @Resource
    private GateApi gateApi;
    @Resource
    private UserApi userApi;



    public ShopDistributionVo cardList(String shopId) {
        ShopDistribution sd = shopDistributionDao.selectByShopId(shopId);
        if (null == sd) {
            Shop shop = shopDao.selectByPrimaryKey(shopId);
            ShopDistributionVo vo = new ShopDistributionVo();
            String addr = fillAddr(shop.getProvinceName(),shop.getCityName(),shop.getDistrictName(),shop.getAddress());
            vo.setSelfMentionTag(shop.getSelfMentionTag());
            vo.setLocalDeliveryTag(shop.getLocalDeliveryTag());
            vo.setLogisticsTag(shop.getLogisticsTag());
            vo.setShopId(shopId);
            vo.setTakeAddr(addr);
            vo.setLocalAddr(addr);
            vo.setExpressAddr(addr);
            vo.setBusBegin(shop.getBusBegin());
            vo.setBusEnd(shop.getBusEnd());
            return vo;
        }
        return BeanConverUtils.convertBean(sd, ShopDistributionVo.class);
    }

    public ShopDistributionVo get(String id, Integer type) {
        ShopDistribution sd = shopDistributionDao.selectByPrimaryKey(id);
        ParamException.isNull(sd);
        ShopDistributionVo vo = BeanConverUtils.convertBean(sd, ShopDistributionVo.class);
        if (ShopDistributionEnum.DistributionType.EXPRESS.getVal().equals(type)) {
            setDistributionConfig(sd.getId(), vo);
            setProvinceVo(vo);
        }
        return vo;
    }

    private void setDistributionConfig(String distributionId, ShopDistributionVo vo) {
        List<DistributionConfig> list = distributionConfigDao.selectByShopDistributionId(distributionId);
        if (!CollectionUtils.isEmpty(list)) {
            Map<String, Object> map = list.stream().collect(Collectors.toMap(DistributionConfig::getName, DistributionConfig::getVal, (key1, key2) -> key2));
            vo.setField(map);
        }
    }

    private void setProvinceVo(ShopDistributionVo vo) {
        if (StrUtil.isNotEmpty(vo.getFreeList())) {
            List<RegionVo> provinceVo = gateApi.listLegionByIds(vo.getFreeList()).getResult();
            vo.setFreeProvinceList(BeanConverUtils.convertBeanList(provinceVo, ProvinceVo.class));
        }
        if (StrUtil.isNotEmpty(vo.getSpecialList())) {
            List<RegionVo> specialProvinceVo = gateApi.listLegionByIds(vo.getSpecialList()).getResult();
            vo.setSpecialProvinceList(BeanConverUtils.convertBeanList(specialProvinceVo, ProvinceVo.class));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Integer onOrOff(DistributionOnOrOffBo bo) {
        ShopDistribution sd = shopDistributionDao.selectByPrimaryKey(bo.getId());
        ParamException.isNull(sd,"该商家配送信息不存在");
        ParamException.isFalse(whetherClosed(sd, bo),"必须开启一种配送方式");
        if (BaseEnum.StatusEnum.ENABLE.getCode().equals(bo.getLocalDeliveryTag()) && null == sd.getLocalSendTime()) {
            return 0;
        }
        if (BaseEnum.StatusEnum.ENABLE.getCode().equals(bo.getLogisticsTag()) && null == sd.getExpressSendTime()) {
            return 0;
        }
        ShopDistribution update = BeanConverUtils.convertBean(bo, ShopDistribution.class,"shopId");
        shopDistributionDao.updateByPrimaryKeySelective(update);
        Shop shop = new Shop();
        shop.setId(sd.getShopId());
        shop.setSelfMentionTag(bo.getSelfMentionTag());
        shop.setLocalDeliveryTag(bo.getLocalDeliveryTag());
        shop.setLogisticsTag(bo.getLogisticsTag());
        shopDao.updateByPrimaryKeySelective(shop);
        return new Integer(1);
    }

    @Transactional(rollbackFor = Exception.class)
    public String setSelfMention(SelfMentionBo bo) {
        ParamException.isTrue((null==bo||StrUtil.isEmpty(bo.getShopId())));
        ShopDistribution sd = BeanConverUtils.convertBean(bo, ShopDistribution.class);
        sd.setSelfMentionTag(BaseEnum.StatusEnum.ENABLE.getCode());
        setShopDistribution(sd);
        return sd.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    public String setLocalDelivery(LocalDeliveryBo bo) {
        ParamException.isTrue((null==bo||StrUtil.isEmpty(bo.getShopId())));
        if (bo.getBeyondWay() == 1) {
            ParamException.isNull(bo.getCityExpressPrice(),"请填写同城快递价格");
        }
        String[] strings = geoLocation(null, bo.getLocalAddr());
        ShopDistribution sd = BeanConverUtils.convertBean(bo, ShopDistribution.class);
        if (null != strings && strings.length > 1) {
            sd.setLocalLng(strings[0]);
            sd.setLocalLat(strings[1]);
        }
        sd.setLocalDeliveryTag(BaseEnum.StatusEnum.ENABLE.getCode());
        setShopDistribution(sd);
        return sd.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    public String setExpress(LogisticsBo bo) {
        verifyParams(bo);
        String[] strings = geoLocation(null, bo.getExpressAddr());
        ShopDistribution sd = BeanConverUtils.convertBean(bo, ShopDistribution.class);
        if (null != strings && strings.length > 1) {
            sd.setExpressLng(strings[0]);
            sd.setExpressLat(strings[1]);
        }
        sd.setLogisticsTag(BaseEnum.StatusEnum.ENABLE.getCode());
        setShopDistribution(sd);
        saveDistributionConfig(bo, sd.getId());
        return sd.getId();
    }

    private void verifyParams(LogisticsBo bo) {
        ParamException.isTrue((null==bo||StrUtil.isEmpty(bo.getShopId())));
        if (BaseEnum.StatusEnum.DISABLE.getCode().equals(bo.getFreeTag())) {
            ParamException.isNull(bo.getBasePostage(),"请完善基础信息");
            bo.setFreeArea(ShopDistributionEnum.FreeArea.NO.getCode());
        }
        if (ShopDistributionEnum.FreeArea.FREE_PROVINCE.equals(bo.getFreeArea())) {
            ParamException.isNull(bo.getFreeList(),"请选择包邮省份");
        }
        if (StrUtil.isNotEmpty(bo.getFreeList())) {
            ParamException.isNull(bo.getBasePostage(),"请完善其它省份邮费信息");
        }
        if (ShopDistributionEnum.WaveType.QUANTITY.getCode().equals(bo.getWaveType())) {
            ParamException.isTrue(null== bo.getAddNumber()||null== bo.getAddNumPrice(),"请完善波动邮费信息");
        }
        if (StrUtil.isNotEmpty(bo.getSpecialList())) {
            ParamException.isNull(bo.getSpecialBasePostage(),"请完善特殊省份邮费信息");
        }
        if (ShopDistributionEnum.WaveType.QUANTITY.getCode().equals(bo.getSpecialWaveType())) {
            ParamException.isTrue(null== bo.getSpecialAddNumber()||null== bo.getSpecialAddPrice(),"请完善特殊省份波动邮费信息");
        }
    }

    public Map<String, DistributionFeeVo> fee(List<DistributionFeeBo> boList) {
        Map<String, DistributionFeeVo> map = new HashMap<>();
        for (DistributionFeeBo bo : boList) {
            DistributionFeeVo vo = getDistributionFeeVo(bo);
            map.put(vo.getShopId(), vo);
        }
        return map;
    }

    private DistributionFeeVo getDistributionFeeVo(DistributionFeeBo bo) {
        ShopDistribution sd = shopDistributionDao.selectByShopId(bo.getShopId());
        ParamException.isNull(sd);
        UserAddrVo addrVo = userApi.getUserAdderDetail(bo.getUserAddrId()).getResult();
        ParamException.isNull(addrVo,"用户收货地址信息出错");
        DistributionFeeVo vo = new DistributionFeeVo();
        vo.setShopId(bo.getShopId());
        vo.setType(bo.getType());
        if (ShopDistributionEnum.DistributionType.LOCAL_DELIVERY.getVal().equals(bo.getType())) {
            ParamException.isFalse(BaseEnum.StatusEnum.ENABLE.getCode().equals(sd.getLocalDeliveryTag()),"该商家未开启同城配送");
            String addr = fillAddr(addrVo.getProvinceName(), addrVo.getCityName(), addrVo.getDistrictName(), addrVo.getAddress());
            String[] loc = geoLocation(addrVo.getCityName(), addr);
            String regionId = addrVo.getRegionId().substring(0, 4);
            localDeliveryFee(sd, vo,bo.getAmount(), loc,regionId);
        }
        if (ShopDistributionEnum.DistributionType.EXPRESS.getVal().equals(bo.getType())) {
            String regionId = addrVo.getRegionId().substring(0, 2) + "0000";
            ShopDistributionVo sdVo = BeanConverUtils.convertBean(sd, ShopDistributionVo.class);
            setDistributionConfig(sd.getId(), sdVo);
            if (BaseEnum.StatusEnum.ENABLE.getCode().equals(sd.getFreeTag())) {
                freePostage(bo.getQuantity(),regionId, sdVo, vo);
            }else{
                notFreePostage(bo.getQuantity(), vo, regionId, sdVo);
            }
        }
        return vo;
    }

    /**
     * 修复之前店铺的配送信息
     */
    public void repair() {
        List<Shop> shops = shopDao.selectAll();
        List<String> shopIds = shops.stream().map(Shop::getId).collect(Collectors.toList());
        List<ShopDistribution> sdList = shopDistributionDao.selectByShopIds(shopIds);
        shops.forEach(shop -> {
            ShopDistribution shopDistribution = sdList.stream().filter(sd -> shop.getId().equals(sd.getShopId())).findFirst().orElse(null);
            if (null == shopDistribution) {
                setDefault(shop.getId(),shop.getLocalDeliveryTag(),shop.getLogisticsTag(),shop.getRegionId(),shop.getProvinceName(),shop.getCityName(),shop.getDistrictName(),shop.getAddress(),shop.getLng(),shop.getLat(),shop.getBusBegin(),shop.getBusEnd());
            }
        });
    }

    public List<ShopDistribution> getByShopIds(List<String> ids) {
        return shopDistributionDao.getByShopIds(ids);
    }

    public void setDefault(String shopId,Integer localDeliveryTag,Integer logisticsTag,String regionId, String province,String city,String district,String address,Double lngD,Double latD, String busBegin, String busEnd) {
        String addr=fillAddr(province, city, district, address);
        String lng = "";
        String lat = "";
        if (null == lngD || BigDecimal.ZERO.compareTo(new BigDecimal(lngD.doubleValue())) >= 0 || null == latD || BigDecimal.ZERO.compareTo(new BigDecimal(latD.doubleValue())) >= 0) {
            String[] strings = geoLocation(city, addr);
            if (null != strings && strings.length > 1) {
                lng = strings[0];
                lat = strings[1];
            }
        }else{
            NumberFormat nf = NumberFormat.getInstance();
            nf.setGroupingUsed(false);
            if (null != lngD) {
                lng = nf.format(lngD);
            }
            if (null != latD) {
                lat = nf.format(latD);
            }
        }
        ShopDistribution sd = new ShopDistribution();
        sd.setShopId(shopId);
        sd.setSelfMentionTag(BaseEnum.StatusEnum.ENABLE.getCode());
        sd.setTakeAddr(addr);
        sd.setBusBegin(busBegin);
        sd.setBusEnd(busEnd);
        if (BaseEnum.StatusEnum.ENABLE.getCode().equals(localDeliveryTag)) {
            sd.setLocalDeliveryTag(BaseEnum.StatusEnum.ENABLE.getCode());
            sd.setLocalSendTime(2);
            sd.setFreeDistance(10);
            sd.setBeyondDistance(10);
            sd.setBeyondWay(0);
        }else{
            sd.setLocalDeliveryTag(BaseEnum.StatusEnum.DISABLE.getCode());
        }
        sd.setLocalAddr(addr);
        sd.setLocalRegionId(regionId);
        sd.setLocalLng(lng);
        sd.setLocalLat(lat);
        if (BaseEnum.StatusEnum.ENABLE.getCode().equals(logisticsTag)) {
            sd.setLogisticsTag(BaseEnum.StatusEnum.ENABLE.getCode());
            sd.setExpressSendTime(2);
            sd.setFreeTag(BaseEnum.StatusEnum.ENABLE.getCode());
            sd.setFreeArea(ShopDistributionEnum.FreeArea.FREE_ALL.getCode());
        }else{
            sd.setLogisticsTag(BaseEnum.StatusEnum.DISABLE.getCode());
        }
        sd.setExpressAddr(addr);
        sd.setExpressLng(lng);
        sd.setExpressLat(lat);
        shopDistributionDao.insert(sd);
    }

    public void updateShopDistributionByShopId(ShopDistribution sd,String shopId) {
        shopDistributionDao.updateShopDistributionByShopId(sd,shopId);
    }

    private void localDeliveryFee(ShopDistribution sd, DistributionFeeVo vo,BigDecimal amount, String[] loc,String regionId) {
        if (StrUtil.isNotEmpty(sd.getLocalRegionId()) && !regionId.equals(sd.getLocalRegionId().substring(0,4))) {
            vo.setMsg("您的收货地址不在同城范围内，请选择其它配送方式");
            vo.setSendTag(BaseEnum.StatusEnum.DISABLE.getCode());
            return;
        }
        if (null != sd.getLocalMinAmount() && null != amount && amount.compareTo(sd.getLocalMinAmount()) < 0) {
            vo.setMsg("订单金额满："+sd.getLocalMinAmount().toString()+"起送");
            vo.setSendTag(BaseEnum.StatusEnum.DISABLE.getCode());
            return;
        }
        if (null != loc && loc.length > 1 && StrUtil.isNotEmpty(sd.getLocalLat()) && StrUtil.isNotEmpty(sd.getLocalLng())) {
            double distanceD = PositionUtil.getDistance(Double.valueOf(loc[1]), Double.valueOf(loc[0]), Double.valueOf(sd.getLocalLat()), Double.valueOf(sd.getLocalLng()));
            Integer distance = new Double(Math.ceil(distanceD)).intValue();
            vo.setDistance(distance);
            vo.setBeyondDistance(sd.getBeyondDistance());
            vo.setFreeDistance(sd.getFreeDistance());
            if (sd.getBeyondDistance() < distance) {
                if (ShopDistributionEnum.BeyondWay.NO.getCode().equals(sd.getBeyondWay())) {
                    vo.setMsg("超出最大配送距离，商家不支持配送");
                    vo.setSendTag(BaseEnum.StatusEnum.DISABLE.getCode());
                }
                if (ShopDistributionEnum.BeyondWay.CITY_EXPRESS.getCode().equals(sd.getBeyondWay())) {
                    vo.setMsg("超出最大配送距离，商家以同城快递方式配送");
                    vo.setFee(sd.getCityExpressPrice());
                    vo.setSendTag(BaseEnum.StatusEnum.ENABLE.getCode());
                }
            }else if (sd.getFreeDistance() < distance) {
                int multiple;
                Integer diffDistance = distance - sd.getFreeDistance();
                if (diffDistance % sd.getAddDistance() == 0) {
                    multiple = diffDistance / sd.getAddDistance();
                }else{
                    multiple = (diffDistance / sd.getAddDistance())+1;
                }
                vo.setFee(sd.getAddPrice().multiply(new BigDecimal(multiple)));
                vo.setSendTag(BaseEnum.StatusEnum.ENABLE.getCode());
            }else{
                vo.setMsg("免费配送范围内，商家免费配送");
                vo.setFee(BigDecimal.ZERO);
                vo.setSendTag(BaseEnum.StatusEnum.ENABLE.getCode());
            }
        }else{
            vo.setMsg("商家发货地址信息有误，请选择其它配送方式");
            vo.setSendTag(BaseEnum.StatusEnum.DISABLE.getCode());
        }
    }

    /**
     * 包邮情况下计算邮费
     */
    private void freePostage(Integer quantity, String regionId, ShopDistributionVo sdVo, DistributionFeeVo vo) {
        if (ShopDistributionEnum.FreeArea.FREE_ALL.getCode().equals(sdVo.getFreeArea())) {
            vo.setMsg("该商家全国包邮");
            vo.setFee(BigDecimal.ZERO);
            vo.setSendTag(BaseEnum.StatusEnum.ENABLE.getCode());
        }
        if (ShopDistributionEnum.FreeArea.FREE_PROVINCE.getCode().equals(sdVo.getFreeArea())) {
            if (StrUtil.isNotEmpty(sdVo.getFreeList())) {
                List<String> list = Arrays.asList(sdVo.getFreeList().split(","));
                if (list.contains(regionId)) {
                    vo.setMsg("该商家对您的收货省份包邮");
                    vo.setFee(BigDecimal.ZERO);
                    vo.setSendTag(BaseEnum.StatusEnum.ENABLE.getCode());
                }else{
                    provinceFee(quantity, vo, sdVo.getWaveType(), sdVo.getBasePostage(), sdVo.getAddNumber(), sdVo.getAddNumPrice());
                }
            }else{
                provinceFee(quantity, vo, sdVo.getWaveType(), sdVo.getBasePostage(), sdVo.getAddNumber(), sdVo.getAddNumPrice());
            }
        }
    }

    /**
     * 不包邮
     */
    private void notFreePostage(Integer quantity, DistributionFeeVo vo, String regionId, ShopDistributionVo sdVo) {
        if (StrUtil.isNotEmpty(sdVo.getSpecialList())) {
            List<String> list = Arrays.asList(sdVo.getSpecialList().split(","));
            if (list.contains(regionId)) {
                provinceFee(quantity, vo, sdVo.getSpecialWaveType(), sdVo.getSpecialBasePostage(), sdVo.getSpecialAddNumber(), sdVo.getSpecialAddPrice());
            }else{
                provinceFee(quantity, vo, sdVo.getWaveType(), sdVo.getBasePostage(), sdVo.getAddNumber(), sdVo.getAddNumPrice());
            }
        }else{
            provinceFee(quantity, vo, sdVo.getWaveType(), sdVo.getBasePostage(), sdVo.getAddNumber(), sdVo.getAddNumPrice());
        }
    }

    /**
     * 省份邮费计算
     */
    private void provinceFee(Integer quantity, DistributionFeeVo vo,Integer waveType, BigDecimal basePostage, Integer addNumber, BigDecimal addPrice) {
        if (ShopDistributionEnum.WaveType.NO.getCode().equals(waveType)) {
            vo.setFee(basePostage);
            vo.setSendTag(BaseEnum.StatusEnum.ENABLE.getCode());
        }
        if (ShopDistributionEnum.WaveType.QUANTITY.getCode().equals(waveType)) {
            int multiple = 0;
            if (quantity > addNumber) {
                multiple = (quantity-addNumber) / addNumber;
            }
            BigDecimal postage = addPrice.multiply(new BigDecimal(multiple)).add(basePostage);
            vo.setFee(postage);
            vo.setSendTag(BaseEnum.StatusEnum.ENABLE.getCode());
        }
    }

    private String[] geoLocation(String cityName, String addr) {
        Object result = gateApi.geoLoc(cityName, addr).getResult();
        if (result != null) {
            JSONArray ja=(JSONArray) result;
            if (null != ja && ja.size() > 0) {
                JSONObject job = ja.getJSONObject(0);
                if (job.get("location") != null) {
                    return job.get("location").toString().split(",");
                }
            }
        }
        return null;
    }

    private String fillAddr(String province,String city,String district,String address) {
        StringBuilder sb = new StringBuilder();
        if (StrUtil.isNotEmpty(province)) {
            sb.append(province);
        }
        if (StrUtil.isNotEmpty(city)) {
            sb.append(city);
        }
        if (StrUtil.isNotEmpty(district)) {
            sb.append(district);
        }
        if (StrUtil.isNotEmpty(address)) {
            sb.append(address);
        }
        return sb.toString();
    }

    private Boolean whetherClosed(ShopDistribution sd, DistributionOnOrOffBo bo) {
        if (BaseEnum.StatusEnum.DISABLE.getCode().equals(bo.getSelfMentionTag()) && BaseEnum.StatusEnum.DISABLE.getCode().equals(sd.getLocalDeliveryTag()) && BaseEnum.StatusEnum.DISABLE.getCode().equals(sd.getLogisticsTag())) {
            return Boolean.FALSE;
        }
        if (BaseEnum.StatusEnum.DISABLE.getCode().equals(bo.getLocalDeliveryTag()) && BaseEnum.StatusEnum.DISABLE.getCode().equals(sd.getSelfMentionTag()) && BaseEnum.StatusEnum.DISABLE.getCode().equals(sd.getLogisticsTag())) {
            return Boolean.FALSE;
        }
        if (BaseEnum.StatusEnum.DISABLE.getCode().equals(bo.getLogisticsTag()) && BaseEnum.StatusEnum.DISABLE.getCode().equals(sd.getLocalDeliveryTag()) && BaseEnum.StatusEnum.DISABLE.getCode().equals(sd.getSelfMentionTag())) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    private void saveDistributionConfig(LogisticsBo bo, String shopDistributionId) {
        if (StrUtil.isNotEmpty(bo.getId())) {
            distributionConfigDao.deleteByShopDistributionId(bo.getId());
        }
        List<DistributionConfig> list = new ArrayList<>();
        Map<String, Object> map = bo.toMap();
        if (!CollectionUtils.isEmpty(map)) {
            for (Map.Entry entry : map.entrySet()) {
                DistributionConfig dc = new DistributionConfig();
                dc.setShopDistributionId(shopDistributionId);
                dc.setName((String) entry.getKey());
                dc.setVal(String.valueOf(entry.getValue()));
                list.add(dc);
            }
        }
        distributionConfigDao.insertList(list);
    }

    private void setShopDistribution(ShopDistribution sd) {
        if (StrUtil.isNotEmpty(sd.getId())) {
            shopDistributionDao.updateByPrimaryKeySelective(sd);
        }else{
            shopDistributionDao.insert(sd);
        }
    }

}
